package edu.aynu.core;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.aynu.exception.UnReasonException;
import edu.aynu.mock.DefaultBankersData;
import org.springframework.util.SerializationUtils;

import java.util.Arrays;
import java.util.List;

/**
 * company: www.abc.com
 * Author: Administrator
 * Create Data: 2021/5/10 0010
 *
 * 银行家算法是一种最有代表性的避免死锁的算法。在避免死锁
 * 方法中允许进程动态地申请资源，但系统在进行资源分配之前，
 * 应先计算此次分配资源的安全性，若分配不会导致系统进入不
 * 安全状态，则分配，否则等待。
 */
public class Bankers{

    /**
     * 可利用资源向量Available
     */
    private int[] available;

    /**
     * 最大需求矩阵Max
     */
    private int[][] max;

    /**
     * 分配矩阵Allocation
     */
    private int[][] allocation;

    /**
     * 需求矩阵Need
     * 强调：Need[i,j]必须等于Max[i,j]-Allocation[i,j]
     */
    private int[][] need;

    /**
     * 贷款者名字
     */
    private String[] name;

    private Safety safety;

    public Bankers() {
        this.available = DefaultBankersData.getAvailable();
        this.max = DefaultBankersData.getMax();
        this.allocation = DefaultBankersData.getAllocation();
        this.need = DefaultBankersData.getNeed();
        this.name = DefaultBankersData.getName();
    }


    /**
     *  银行尝试贷款给客户
     * @return 成功返回true,失败返回false
     */
    public boolean loan(int index, int[] request) {

        if (!checkRequest(index, request)) {
            return false;
        }
        Bankers possibility = tryAllocationResource(index, request);
        if (!safetyInspection(possibility)) {
            return false;
        } else {
            allocationResource(index, request);
        }
        return true;
    }

    /**
     * 安全性检查
     * @return 成功返回true,失败返回false
     */
    private boolean safetyInspection(Bankers bankers) {
        this.safety = new Safety(bankers);
        return safety.isSafe();
    }


    /**
     * 试探性分配资源
     * @return 试探后的Bankers
     */
    private Bankers tryAllocationResource(int index, int[] request) {
        Bankers bankers = null;
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            bankers = objectMapper.readValue(objectMapper.writeValueAsString(this), Bankers.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < request.length; i++) {
            bankers.available[i] -= request[i];
            bankers.allocation[index][i] += request[i];
            bankers.need[index][i] -= request[i];
        }
        return bankers;
    }
    /**
     * 分配资源
     */
    private void allocationResource(int index, int[] request) {
        for (int i = 0; i < request.length; i++) {
            this.available[i] -= request[i];
            this.allocation[index][i] += request[i];
            this.need[index][i] -= request[i];
        }
    }

    /**
     *  检查请求是否合法
     * @return 成功返回true,失败返回false
     */
    private boolean checkRequest(int index, int[] request) {
        if (request == null || request.length == 0) {
            return false;
        }
        if (request.length != available.length) {
            return false;
        }
        for (int i = 0; i < request.length; i++) {

            if (request[i] < 0) {
                throw new UnReasonException("请求数据不合法,request[" + i + "]:" +
                        request[i]);
            }
            if (request[i] > need[index][i]) {
                throw new UnReasonException("请求数据高于需求,request[" + i + "]:" +
                        request[i] + ",need[" + i +"]:" + need[index][i]);
            }
            if (request[i] > available[i]) {
                throw new UnReasonException("请求数据高于剩余资源,request[" + i + "]:" +
                        request[i] + ",available[" + i +"]:" + available[i]);
            }
        }

        return true;
    }

    public Bankers(int[] available, int[][] max, int[][] allocation, int[][] need, String[] name) {
        this.available = available;
        this.max = max;
        this.allocation = allocation;
        this.need = need;
        this.name = name;
    }

    public int[] getAvailable() {
        return available;
    }

    public int[][] getMax() {
        return max;
    }

    public int[][] getAllocation() {
        return allocation;
    }

    public int[][] getNeed() {
        return need;
    }

    public String[] getName() {
        return name;
    }


    public void setAvailable(int[] available) {
        this.available = available;
    }

    public void setMax(int[][] max) {
        this.max = max;
    }

    public void setAllocation(int[][] allocation) {
        this.allocation = allocation;
    }

    public void setNeed(int[][] need) {
        this.need = need;
    }

    public void setName(String[] name) {
        this.name = name;
    }

    public void setSafety(Safety safety) {
        this.safety = safety;
    }

    public Safety getSafety() {
        return safety;
    }

    @Override
    public String toString() {
        return "Bankers{" +
                "available=" + Arrays.toString(available) +
                ", max=" + Arrays.toString(max) +
                ", allocation=" + Arrays.toString(allocation) +
                ", need=" + Arrays.toString(need) +
                ", name=" + Arrays.toString(name) +
                '}';
    }
}
